// @Author EthanScriptOn
// @Desc
package safe

import (
	"gitee.com/fatzeng/srf_switch_basic_components/identity"
	"sync"
	"time"
)

// SafeGo It can be restarted after a panic occurs in GO
type SafeGo struct {
	abnormalList  sync.Map
	abnormalRetry sync.Map
	fIdentity     identity.IdentityInformation
}

func GenerateSafeGo() *SafeGo {
	snowflake, err := identity.GenerateSnowflake(&identity.SnowflakeConfig{Epoch: time.Now(), SequenceBits: 12})
	if err != nil {
		panic(err)
	}
	return &SafeGo{
		fIdentity: snowflake,
	}
}

// executeGo execute
func (s *SafeGo) executeGo(fIdentity interface{}, maxRetries int, f func()) {
	// +1 number of retries
	retryCount, _ := s.abnormalRetry.LoadOrStore(fIdentity, 0)
	s.abnormalRetry.Store(fIdentity, retryCount.(int)+1)

	go func() {
		defer func() {
			// Exception recovery
			if r := recover(); r != nil {

				// The number of retries is met
				currentRetry, _ := s.abnormalRetry.Load(fIdentity)
				if maxRetries != -1 && currentRetry.(int) >= maxRetries {
					return
				}

				// Abnormal recovery
				fBack, _ := s.abnormalList.Load(fIdentity)
				s.executeGo(fIdentity, maxRetries, fBack.(func()))
			}
		}()

		// Objective function execution
		f()

	}()
}

// SafeGo A function that starts a recoverable goroutine with no retry
func (s *SafeGo) SafeGo(f func()) error {
	identifier, err := s.fIdentity.GenerateUniqueIdentifier()
	if err != nil {
		return err
	}
	s.executeGo(identifier, -1, f)
	return nil
}

// SafeGoRetry A function that starts a recoverable goroutine with retry
func (s *SafeGo) SafeGoRetry(maxRetries int, f func()) error {
	identifier, err := s.fIdentity.GenerateUniqueIdentifier()
	if err != nil {
		return err
	}
	s.executeGo(identifier, maxRetries, f)
	return nil
}
